package com.vr.cloudpicture.manager.auth;

import cn.dev33.satoken.stp.StpInterface;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.ObjUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.ReflectUtil;
import cn.hutool.core.util.StrUtil;
import cn.hutool.extra.servlet.ServletUtil;
import cn.hutool.http.ContentType;
import cn.hutool.http.Header;
import cn.hutool.json.JSONUtil;
import com.vr.cloudpicture.constant.UserConstant;
import com.vr.cloudpicture.enums.SpaceRoleEnum;
import com.vr.cloudpicture.enums.SpaceTypeEnum;
import com.vr.cloudpicture.exception.BusinessException;
import com.vr.cloudpicture.exception.ErrorCode;
import com.vr.cloudpicture.exception.ThrowUtils;
import com.vr.cloudpicture.manager.auth.model.SpaceUserContext;
import com.vr.cloudpicture.manager.auth.model.SpaceUserPermissionConstant;
import com.vr.cloudpicture.model.dto.user.UserLogin;
import com.vr.cloudpicture.model.entity.Picture;
import com.vr.cloudpicture.model.entity.Space;
import com.vr.cloudpicture.model.entity.SpaceUser;
import com.vr.cloudpicture.service.IPictureService;
import com.vr.cloudpicture.service.ISpaceService;
import com.vr.cloudpicture.service.ISpaceUserService;
import com.vr.cloudpicture.service.IUserService;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Component;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;

import javax.servlet.http.HttpServletRequest;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import java.util.Map;

/**
 * 自定义权限加载接口实现类
 *
 * @author hzh
 * @date 2025/05/27
 */
// 保证此类被 SpringBoot 扫描，完成 Sa-Token 的自定义权限验证扩展
@Component
@RequiredArgsConstructor
public class StpInterfaceImpl implements StpInterface  {
    @Value("${server.servlet.context-path}")
    private String contextPath;
    private final SpaceUserAuthManager spaceUserAuthManager;
    private final ISpaceUserService spaceUserService;
    private final IPictureService pictureService;
    private final IUserService userService;
    private final ISpaceService spaceService;

    /**
     * 返回一个账号所拥有的权限码集合
     */
    @Override
    public List<String> getPermissionList(Object loginId, String loginType) {
        // 基于权限校验
        // 只校验空间权限
        if (!StpKit.SPACE_TYPE.equals(loginType)) {
            // 不是就是没有任何权限
            return Collections.emptyList();
        }
        // 获取管理员权限列表
        List<String> adminPermissions = spaceUserAuthManager.getPermissionsByRole(SpaceRoleEnum.ADMIN.getValue());
        // 获取当前请求的上下文对象
        SpaceUserContext spaceUserContext = getSpaceUserContextByRequest();
        // 都为字段都为空的话表示访问的是公共图库，对所有人开放所有权限，也就是管理员
        if (isAllFieldsNull(spaceUserContext)) {
            return adminPermissions;
        }
        // 通过loginId获取用户信息
        UserLogin userLogin = (UserLogin) StpKit.SPACE.getSessionByLoginId(loginId).get(UserConstant.USER_LOGIN_STATE);
        if (userLogin==null) {
            throw  new BusinessException(ErrorCode.NOT_AUTH_ERROR, "用户未登录");
        }
        Long userId = userLogin.getId();
        // 优先从上下文里获取spaceUser对象
        SpaceUser spaceUser = spaceUserContext.getSpaceUser();
        if (spaceUser != null) {
            return spaceUserAuthManager.getPermissionsByRole(spaceUser.getSpaceRole());
        }
        // 没有spaceUser对象，则从数据库中获取
        Long spaceUserId = spaceUserContext.getSpaceUserId();
        if (spaceUserId != null) {
            // 查询数据库获取spaceUser对象
            spaceUser = spaceUserService.getById(spaceUserId);
            ThrowUtils.throwIf(spaceUser == null, ErrorCode.NOT_FOUND_ERROR,"未找到空间用户信息");
            // 不为空时，判断当前登录的用户是否属于该空间
            SpaceUser loginSpaceUser = spaceUserService.lambdaQuery()
                    .eq(SpaceUser::getUserId, userId)
                    .eq(SpaceUser::getSpaceId, spaceUserContext.getSpaceId())
                    .one();
            // 不属于该空间的话没权限
            if (loginSpaceUser==null) {
                return Collections.emptyList();
            }
            return spaceUserAuthManager.getPermissionsByRole(loginSpaceUser.getSpaceRole());
        }
        // 没有spaceUserId,就用空间id或者图片id来查询
        Long pictureId = spaceUserContext.getPictureId();
        Long spaceId = spaceUserContext.getSpaceId();
        if (spaceId==null) {
            if (pictureId==null) {
                // 表示访问的不是空间或者图库
                return adminPermissions;
            }
            Picture picture = pictureService.getById(pictureId);
            ThrowUtils.throwIf(picture==null, ErrorCode.NOT_FOUND_ERROR,"未找到图片信息");
            // 有图片信息但是没有空间id，表示在公共图库，公共图库的图片里的图片仅本人或者管理员能够编辑
             spaceId = picture.getSpaceId();
             if (spaceId==null) {
                 if (userId.equals(picture.getUserId()) || userService.isAdmin(userLogin)) {
                     return adminPermissions;
                 } else {
                     // 不是自己的图片,仅支持浏览
                     return Collections.singletonList(SpaceUserPermissionConstant.PICTURE_VIEW);
                 }
             }
        }
        // 有空间id的话,看空间是否存在
        Space space = spaceService.getById(spaceId);
        ThrowUtils.throwIf(space==null, ErrorCode.NOT_FOUND_ERROR,"未找到空间信息");
        // 判断空间类型
        if (SpaceTypeEnum.PRIVATE.getValue().equals(space.getSpaceType())) {
            // 私有空间，创建空间本人或者管理员能够编辑
            if (userId.equals(space.getUserId()) || userService.isAdmin(userLogin)) {
                return adminPermissions;
            } else {
                // 否则，没有任何权限
                return Collections.emptyList();
            }
        }
        // 团队空间的话,通过userId和spaceId来查询来获取当前用户在空间的权限
         spaceUser = spaceUserService.lambdaQuery()
                .eq(SpaceUser::getUserId, userId)
                .eq(SpaceUser::getSpaceId, spaceId)
                .one();
        if (spaceUser==null) {
            // 你压根不是这个空间的成员
            return Collections.emptyList();
        }
        return spaceUserAuthManager.getPermissionsByRole(spaceUser.getSpaceRole());
    }

    /**
     * 校验对象属性是否都为null
     */
    private boolean isAllFieldsNull(Object object) {
        if (object == null) {
            // 对象本身都为空
            return true;
        }
        return Arrays.stream(ReflectUtil.getFields(object.getClass()))
                // 获取每个字段的值
                .map(field -> ReflectUtil.getFieldValue(object, field))
                // 判断属性值是否为空
                .allMatch(ObjectUtil::isEmpty);
    }

    /**
     * 返回一个账号所拥有的角色标识集合 (权限与角色可分开校验)
     */
    @Override
    public List<String> getRoleList(Object loginId, String loginType) {
        // 不用角色进行鉴权而用权限
        return Collections.emptyList();
    }

    /**
     * 从请求中获取上下文对象
     */
    private SpaceUserContext getSpaceUserContextByRequest() {
        HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.currentRequestAttributes()).getRequest();
        String contentType = request.getHeader(Header.CONTENT_TYPE.getValue());
        // 根据contentType来判断是post请求还是get请求来兼容get和post请求
        SpaceUserContext authRequest;
        if (ContentType.JSON.getValue().equals(contentType)) {
            // post请求
            String body = ServletUtil.getBody(request);
            authRequest = JSONUtil.toBean(body, SpaceUserContext.class);
        } else {
            // get请求
            Map<String, String> paramMap = ServletUtil.getParamMap(request);
            authRequest = BeanUtil.toBean(paramMap, SpaceUserContext.class);
        }
        // 根据路径区分id字段的含义
        Long id = authRequest.getId();
        if (ObjUtil.isNotNull(id)) {
            // url:http://localhost:8123/api/picture/delete
            // uri: /api/picture/delete
            String requestUri = request.getRequestURI();
            // 得到：picture/delete
            String partUri = requestUri.replace(contextPath + "/", "");
            // picture
            String moduleName = StrUtil.subBefore(partUri, "/", false);
            switch (moduleName) {
                case "picture":
                    authRequest.setPictureId(id);
                    break;
                case "space":
                    authRequest.setSpaceId(id);
                    break;
                case "space-user":
                    authRequest.setSpaceUserId(id);
                    break;
                default:
            }
        }
        return authRequest;
    }

}
